package com.dd.common.filter;


import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 全局请求日志过滤器
 * 记录请求方法、路径、参数、耗时、状态码、traceId
 * 仅记录业务接口，过滤 Swagger、静态资源等无关请求
 */
@Slf4j
@Component
public class GlobalRequestLogFilter extends OncePerRequestFilter {

    private static final String TRACE_ID = "X-Trace-ID";

    // 脱敏字段
    private static final Set<String> SENSITIVE_KEYS = Collections.unmodifiableSet(
            new HashSet<>(Arrays.asList(
                    "password", "pwd", "secret", "token", "authorization", "refreshToken"
            ))
    );


    // 要忽略的日志路径（支持 Ant 风格通配符）
    // 注意：AntPathMatcher 是精确前缀匹配的，/webjars/** ≠ /api/webjars/**，
    // 因此如果有自定义上下文服务路径，如 /api，就需要补上 /api
    private static final List<String> EXCLUDE_PATTERNS = Arrays.asList(
            "/swagger-ui/**",
            "/doc.html",
            "/webjars/**",
            "/swagger-resources/**",
            "/v3/api-docs/**",
            "/swagger/**",
            "/knife4j/**",
            "/actuator/**",
            "/favicon.ico",
            "/static/**",
            "/assets/**",
            "/css/**",
            "/js/**",

            // 带 /api 前缀（根据你的实际 context path 修改）
            "/api/swagger-ui/**",
            "/api/swagger-ui.html",
            "/api/swagger-resources/**",
            "/api/v3/api-docs/**",
            "/api/webjars/**",
            "/api/knife4j/**",
            "/api/doc.html",
            "/api/static/**",
            "/api/assets/**",
            "/api/css/**",
            "/api/js/**",
            "/api/favicon.ico",
            "/api/actuator/**"
    );

    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        String requestURI = request.getRequestURI();

        // 1. 判断是否需要过滤（跳过日志记录）
        if (shouldNotFilter(requestURI)) {
            chain.doFilter(request, response);
            return;
        }

        // 2. 包装请求和响应
        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);

        // 3. 生成 traceId
        String traceId = generateTraceId();
        responseWrapper.setHeader(TRACE_ID, traceId);

        // 4. 开始计时
        long startTime = System.currentTimeMillis();

        try {
            // 5. 打印请求日志
            logRequest(traceId, requestWrapper);
            // 6. 继续执行
            chain.doFilter(requestWrapper, responseWrapper);
        } finally {
            // 7. 打印响应日志
            long duration = System.currentTimeMillis() - startTime;
            logResponse(traceId, requestWrapper, responseWrapper, duration);
            // 8. 必须释放资源，否则响应可能被截断
            responseWrapper.copyBodyToResponse();
        }
    }

    /**
     * 判断当前请求是否应被忽略
     */
    private boolean shouldNotFilter(String requestURI) {
        return EXCLUDE_PATTERNS.stream()
                .anyMatch(pattern -> pathMatcher.match(pattern, requestURI));
    }


    /**
     * 记录请求日志
     */
    private void logRequest(String traceId, ContentCachingRequestWrapper request) {
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String queryString = request.getQueryString();
        Map<String, String> params = getParams(request);
        // 获取请求体（POST/PUT）
        String body = getRequestBody(request);
// 脱敏处理
        String safeBody = maskSensitiveData(body);
        Map<String, String> safeParams = maskSensitiveMap(params);

        StringBuilder logMsg = new StringBuilder();
        logMsg.append("[请求] [").append(traceId).append("] ")
                .append("方法: ").append(method).append(", ")
                .append("路径: ").append(uri);

        if (queryString != null) {
            logMsg.append("?").append(queryString);
        }

        if (!safeParams.isEmpty()) {
            logMsg.append(", 参数: ").append(safeParams);
        }

        if (!body.isEmpty()) {
            logMsg.append(", 请求体: ").append(safeBody);
        }

        log.info(logMsg.toString());
    }

    /**
     * 记录响应日志
     */
    private void logResponse(String traceId, HttpServletRequest request,
                             ContentCachingResponseWrapper response, long duration) {
        int status = response.getStatus();
        log.info("[响应] [{}] 路径: {}, 状态: {}, 耗时: {}ms",
                traceId, request.getRequestURI(), status, duration);
    }

    /**
     * 获取请求参数（GET 和 POST 表单）
     */
    private Map<String, String> getParams(HttpServletRequest request) {
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> parameterMap = request.getParameterMap();
        if (parameterMap != null && !parameterMap.isEmpty()) {
            parameterMap.forEach((key, values) ->
                    params.put(key, values.length > 0 ? values[0] : "")
            );
        }
        return params;
    }

    /**
     * 获取请求体（JSON、form-data 等）
     */
    private String getRequestBody(ContentCachingRequestWrapper request) {
        byte[] contentAsByteArray = request.getContentAsByteArray();
        if (contentAsByteArray.length > 0) {
            return new String(contentAsByteArray, StandardCharsets.UTF_8);
        }
        return "";
    }

    /**
     * 脱敏：隐藏敏感字段（如密码）
     */
    private String maskSensitiveData(String json) {
        if (json == null || json.isEmpty()) return json;
        String result = json;
        for (String field : SENSITIVE_KEYS) {
            result = result.replaceAll("(\"" + field + "\"\\s*:\\s*\"?)[^\"]+?(\"?)", "$1***$2");
        }
        return result;
    }

    /**
     * 脱敏：Map 类型参数
     */
    private Map<String, String> maskSensitiveMap(Map<String, String> params) {
        Map<String, String> safe = new HashMap<>(params);
        SENSITIVE_KEYS.forEach(key -> {
            if (safe.containsKey(key)) {
                safe.put(key, "***");
            }
        });
        return safe;
    }

    /**
     * 生成唯一 traceId（可替换为雪花算法、UUID 等）
     */
    private String generateTraceId() {
        return UUID.randomUUID().toString().replace("-", "").substring(0, 16);
    }
}